import subprocess
import os
import sys
import logging
import time
from tendo import singleton  # 用于防止多开
from plyer import notification
import serial.tools.list_ports

import pystray
from pystray import MenuItem as item
from PIL import Image, ImageDraw
import threading
import winreg as reg

# 防止多开
try:
    me = singleton.SingleInstance()  # 确保只有一个实例运行
except singleton.SingleInstanceException:
    sys.exit("程序已运行")

# 标志位，控制线程是否继续运行
running = True

startup_key = r"Software\Microsoft\Windows\CurrentVersion\Run"

# 日志记录设置，使用 UTF-8 编码
log_file = "serial_monitor.log"
logging.basicConfig(
    level=logging.INFO,  # 日志记录级别
    format="%(asctime)s - %(levelname)s - %(message)s",  # 日志格式
    handlers=[
        logging.FileHandler(log_file, encoding='utf-8'),  # 设置文件处理器，并指定编码为 UTF-8
        logging.StreamHandler()  # 可选，输出到控制台
    ]
)

logging.info("程序启动")

"""
    创建一个简单的图标，用于在任务栏中的系统托盘图标显示。
    图标的大小必须是 16x16，否则可能会导致程序崩溃。
    右击能够设置开启/关闭开机启动。
    鼠标停留在图标的上方时，会显示一个提示框。
"""
def create_image():
    """创建托盘图标的图像"""
    # 创建一个 64x64 的透明图片
    width = 64
    height = 64
    image = Image.new('RGBA', (width, height), (255, 255, 255, 0))
    draw = ImageDraw.Draw(image)

    # 绘制图标（一个简单的绿色圆形）
    draw.ellipse((0, 0, width, height), fill=(0, 255, 0))
    return image

def on_quit(icon, item):
    """退出程序时退出托盘图标"""
    global running
    running = False  # 设置线程停止标志
    icon.stop()
    sys.exit()

def toggle_startup(icon, item):
    """切换开机启动项"""
    if is_startup_enabled():
        remove_from_startup()
        icon.title = "serial_monitor - 开机启动已禁用"
    else:
        add_to_startup()
        icon.title = "serial_monitor - 开机启动已启用"

def add_to_startup():
    """将程序添加到 Windows 注册表的开机启动项"""
    program_name = "MyTrayApp"  # 设置应用的名称
    program_path = sys.executable  # 获取当前程序的路径
    try:
        reg_key = reg.OpenKey(reg.HKEY_CURRENT_USER, startup_key, 0, reg.KEY_WRITE)
        reg.SetValueEx(reg_key, program_name, 0, reg.REG_SZ, program_path)
        reg.CloseKey(reg_key)
        print(f"已添加到开机启动: {program_path}")
    except PermissionError:
        print("权限错误：无法修改注册表，程序需要管理员权限才能进行此操作。")
        show_admin_prompt()  # 请求管理员权限
    except Exception as e:
        print(f"添加到开机启动失败: {e}")

def remove_from_startup():
    """从 Windows 注册表中移除开机启动项"""
    program_name = "MyTrayApp"
    try:
        reg_key = reg.OpenKey(reg.HKEY_CURRENT_USER, startup_key, 0, reg.KEY_WRITE)
        reg.DeleteValue(reg_key, program_name)
        reg.CloseKey(reg_key)
        print("已移除开机启动")
    except PermissionError:
        print("权限错误：无法修改注册表，程序需要管理员权限才能进行此操作。")
        show_admin_prompt()  # 请求管理员权限
    except Exception as e:
        print(f"移除开机启动失败: {e}")

def is_startup_enabled():
    """检查程序是否已被添加到开机启动项"""
    program_name = "serial_monitor"
    try:
        reg_key = reg.OpenKey(reg.HKEY_CURRENT_USER, startup_key, 0, reg.KEY_READ)
        reg.QueryValueEx(reg_key, program_name)
        reg.CloseKey(reg_key)
        return True
    except FileNotFoundError:
        return False

def show_admin_prompt():
    """请求管理员权限"""
    ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, sys.argv[0], None, 1)

def run_tray_icon():
    """创建并运行托盘图标"""
    # 创建托盘图标
    icon = pystray.Icon("test_icon", create_image(), title="serial_monitor",menu=pystray.Menu(
        item('切换开机启动', toggle_startup),
        item('退出', on_quit)  # 点击托盘图标右键菜单时触发的操作
    ))

    # 设置初始状态的标题
    if is_startup_enabled():
        icon.title = "serial_monitor - 开机启动已启用"
    else:
        icon.title = "serial_monitor - 开机启动已禁用"

    # 运行图标，进入事件循环
    icon.run()

def get_serial_ports_detail_info(com_port):
    ports = serial.tools.list_ports.comports()
   # 遍历所有串口设备
    for port in ports:
        if port.device == com_port:
            # print(f"找到匹配设备: {port.device}")
            # print(f"名称: {port.name}")
            # print(f"描述: {port.description}")
            # print(f"硬件 ID: {port.hwid}")
            # print(f"制造商: {port.manufacturer}")
            return port  # 返回匹配的端口对象
    # print(f"未找到 COM 号为 {com_port} 的设备")
    return None

def get_serial_ports():
    """获取当前可用的串口列表"""
    ports = serial.tools.list_ports.comports()
    return [port.device for port in ports]

def show_notification(title, message):
    """显示系统通知"""
    notification.notify(
        title=title,
        message=message,
        app_name="串口监控",
        timeout=5  # 通知显示时间（秒）
    )

def monitor_serial_ports():
    """持续监控串口变化"""
    global running
    last_ports = get_serial_ports()
    logging.info(f"当前可用串口: {last_ports}")
    
    while running:
        time.sleep(1)  # 每秒检测一次
        current_ports = get_serial_ports()

        # 检查新增串口
        new_ports = set(current_ports) - set(last_ports)
        if new_ports:
            for port in new_ports:
                detail_info = get_serial_ports_detail_info(port)
                port_info = f"设备: {detail_info.device}\n\t名称: {detail_info.name}\n\t描述: {detail_info.description}\n\t制造商: {detail_info.manufacturer}"
                logging.info(f"检测到新串口接入: \n\t{port_info}")
                show_notification("新串口接入", f"检测到新串口接入: {port_info}")

        # 检查移除的串口
        removed_ports = set(last_ports) - set(current_ports)
        if removed_ports:
            for port in removed_ports:
                logging.info(f"检测到串口移除: {port}")
                show_notification("串口移除", f"检测到串口移除: {port}")

        last_ports = current_ports

if __name__ == "__main__":
    try:

        # 启动串口监控线程
        serial_thread = threading.Thread(target=monitor_serial_ports)
        serial_thread.daemon = True  # 设置为守护线程，主程序退出时自动结束
        serial_thread.start()

        # 启动系统托盘图标
        run_tray_icon()
    except KeyboardInterrupt:
        logging.info("程序已退出")
        print("程序已退出")